package daliytool.http.server;

import com.alibaba.fastjson.JSONObject;
import daliytool.WebViewMain;
import daliytool.http.server.response.ResultObject;
import daliytool.http.server.support.StrategyContext;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.handler.codec.http.*;
import io.netty.handler.stream.ChunkedFile;
import io.netty.util.CharsetUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.activation.MimetypesFileTypeMap;
import java.io.File;
import java.io.RandomAccessFile;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE;
import static io.netty.handler.codec.http.HttpResponseStatus.OK;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;

/**
 * @author shuaidongdong
 */
public class HttpServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> {

    private static final Logger logger = LoggerFactory.getLogger(HttpServerHandler.class);

    /**
     * 获取传递的参数
     *
     * @param ctx
     * @param fullHttpRequest
     * @return
     */
    private static Map<String, Object> getParamsFromChannel(ChannelHandlerContext ctx, FullHttpRequest fullHttpRequest) {

        Map<String, Object> mapReturnData = new HashMap<>(10);
        if (fullHttpRequest.method() == HttpMethod.GET) {
            // 处理get请求
            QueryStringDecoder decoder = new QueryStringDecoder(fullHttpRequest.uri());
            Map<String, List<String>> param = decoder.parameters();
            for (Map.Entry<String, List<String>> entry : param.entrySet()) {
                mapReturnData.put(entry.getKey(), entry.getValue().get(0));
            }

        }

        logger.debug("接收到的参数:{}", mapReturnData.toString());
        return mapReturnData;
    }

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, FullHttpRequest fullHttpRequest) {

        if (isDownLoad(fullHttpRequest)) {
            try {
                download(channelHandlerContext, fullHttpRequest);
            } catch (Exception e) {
                logger.error("download error", e);
            }
        } else {
            Map<String, Object> map = getParamsFromChannel(channelHandlerContext, fullHttpRequest);
            ResultObject resultObject = StrategyContext.getResponse(map);
            JSONObject jsonRootObj = new JSONObject();
            jsonRootObj.put("data", resultObject);
            FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK);
            response.headers().set(CONTENT_TYPE, "application/json; charset=UTF-8");
            response.headers().set("Access-Control-Allow-Origin", "*");
            StringBuilder builder = new StringBuilder();
            builder.append(jsonRootObj.toJSONString());
            ByteBuf buffer = Unpooled.copiedBuffer(builder, CharsetUtil.UTF_8);
            response.content().writeBytes(buffer);
            buffer.release();
            channelHandlerContext.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
        }

    }

    private boolean isDownLoad(FullHttpRequest fullHttpRequest) {
        return fullHttpRequest.uri().contains("/download");
    }

    private void download(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
        HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);

        try {
            String basePath = WebViewMain.class.getResource("index.html").getPath();
            basePath = basePath.substring(0, basePath.lastIndexOf("/"));
            final String uri = request.uri();
            final String path = sanitizeUri(uri, basePath);
            if (StringUtils.isNotEmpty(path)) {
                File file = new File(path);
                RandomAccessFile raf = new RandomAccessFile(file, "r");
                long fileLength = raf.length();

                HttpUtil.setContentLength(response, fileLength);
                setContentTypeHeader(response, file);

                response.headers().set(HttpHeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN,"*");
                if (HttpUtil.isKeepAlive(request)) {
                    response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderNames.KEEP_ALIVE);
                }
                if (path.endsWith(".js")) {
                    response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain");
                }
                if (path.endsWith(".css")) {
                    response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/css; charset=utf-8");
                }
                ctx.write(response);
                ChannelFuture sendFileFuture;
                ChannelFuture lastContentFuture;
                sendFileFuture = ctx.writeAndFlush(new HttpChunkedInput(new ChunkedFile(raf, 0, fileLength, 8192)), ctx.newProgressivePromise());
                lastContentFuture = sendFileFuture;
                sendFileFuture.addListener(new ChannelProgressiveFutureListener() {
                    @Override
                    public void operationProgressed(ChannelProgressiveFuture future, long progress, long total) {
                        // nothing to do
                    }

                    @Override
                    public void operationComplete(ChannelProgressiveFuture future) {
                       logger.info("下载完成:{}",path);
                    }
                });
                lastContentFuture.addListener(ChannelFutureListener.CLOSE);
            }

        } catch (Exception e) {
            logger.error("下载异常", e);
            ctx.write(response);
            ctx.writeAndFlush("error").addListener(ChannelFutureListener.CLOSE);
        }

    }


    private static String sanitizeUri(String uri, String basePath) {
        String filePath = null;
        String[] path = uri.split("download");
        if (path.length == 2) {
            String suffix = path[1];
            filePath = System.getProperty("user.dir") + suffix;
        }
        return filePath;
    }

    private static void setContentTypeHeader(HttpResponse response, File file) {
        MimetypesFileTypeMap mimeTypesMap = new MimetypesFileTypeMap();
        response.headers().set(CONTENT_TYPE, mimeTypesMap.getContentType(file.getPath()));
    }

}